using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Design.Serialization;
using Microsoft.WowAddonStudio.FrameXml.Components;
using Microsoft.WowAddonStudio.FrameXml.Components.Support;
using Microsoft.WowAddonStudio.FrameXml.Serialization;
using Microsoft.WowAddonStudio.FrameXml.Services;

namespace Microsoft.WowAddonStudio.FrameXml.Design.Serialization
{
	/// <summary>
	/// Provides a class for FrameXml elements that can invoke serialization and deserialization.
	/// </summary>
	public sealed class FrameXmlDesignerSerializationService : IDesignerSerializationService
	{
		private readonly IServiceProvider serviceProvider;
		private readonly ComponentSerializationService serializer;

		/// <summary>
		/// Gets or sets the fram XML container.
		/// </summary>
		/// <value>The fram XML container.</value>
		private IContainer FramXmlContainer { get; set; }

		/// <summary>
		/// Initializes a new instance of the <see cref="FrameXmlDesignerSerializationService"/> class.
		/// </summary>
		/// <param name="serviceProvider">The service provider.</param>
		/// <param name="container">The container.</param>
		public FrameXmlDesignerSerializationService(IServiceProvider serviceProvider, IContainer container)
		{
			if (serviceProvider == null)
				throw new ArgumentNullException("serviceProvider");
			if (container == null)
				throw new ArgumentNullException("container");

			FramXmlContainer = container;
			this.serviceProvider = serviceProvider;
			serializer = (ComponentSerializationService) serviceProvider.GetService(typeof (IFrameXmlComponentSerializationService));

			if (Serializer == null)
				throw new MissingServiceException(Resources.ComponentSerializationServiceExceptionMessage);
		}

		/// <summary>
		/// Gets the service provider.
		/// </summary>
		/// <value>The service provider.</value>
		public IServiceProvider ServiceProvider
		{
			get { return serviceProvider; }
		}

		/// <summary>
		/// Gets the serializer.
		/// </summary>
		/// <value>The serializer.</value>
		public ComponentSerializationService Serializer
		{
			get { return serializer; }
		}

		#region IDesignerSerializationService Members

		/// <summary>
		/// Deserializes the specified serialization data object and returns a collection of objects represented by that data.
		/// </summary>
		/// <param name="serializationData">An object consisting of serialized data.</param>
		/// <returns>
		/// An <see cref="T:System.Collections.ICollection"/> of objects rebuilt from the specified serialization data object.
		/// </returns>
		public ICollection Deserialize(object serializationData)
		{
			if (!(serializationData is SerializationStore))
				throw new BadSerializationObjectException(serializationData);

			if (Serializer == null)
				throw new MissingServiceException(Resources.ComponentSerializationServiceExceptionMessage);

			var components = new List<IComponent>();
			var componentFactory = (FrameXmlComponentFactory) serviceProvider.GetService(typeof (FrameXmlComponentFactory));
			if (componentFactory != null)
			{
				foreach (object obj in Serializer.Deserialize((SerializationStore) serializationData, FramXmlContainer))
				{
					IFrameXmlComponent component = componentFactory.CreateComponent(obj as SerializationObject);
					components.Add(component);
				}
			}
			return components;
		}

		/// <summary>
		/// Serializes the specified collection of objects and stores them in a serialization data object.
		/// </summary>
		/// <param name="objects">A collection of objects to serialize.</param>
		/// <returns>
		/// An object that contains the serialized state of the specified collection of objects.
		/// </returns>
		public object Serialize(ICollection objects)
		{
			if (objects == null)
				throw new ArgumentNullException("objects");

			if (Serializer == null)
				throw new MissingServiceException(Resources.ComponentSerializationServiceExceptionMessage);

			SerializationStore store = Serializer.CreateStore();

			using (store)
			{
				foreach (object obj in objects)
				{
					Serializer.Serialize(store, obj);
				}
				return store;
			}
		}

		#endregion
	}
}